#include "hi_spi.h"
#include "iot_spi.h" 

//定义I2C和SPI的宏
#define SSD1306_USE_IIC 1
#define SSD1306_USE_SPI 0

/* IIC方式控制SSD1306 */
#if SSD1306_USE_IIC
#include "ssd1306_iic_drv.h"

#define SSD1306_I2C_ADDR    (0x3C <<1)
#define OLED_IIC_BAUDRATE  400000
#endif

/* SPI方式控制SSD1306 */
#if SSD1306_USE_SPI
#include "ssd1306_spi_drv.h"

#define  U8_LEN   1
#endif


//OLED显存总共分为8页
//每页8行，一行128个像素点
//OLED的显存
//存放格式如下.
//[0]0 1 2 3 ... 127 (0~7)行	   
//[1]0 1 2 3 ... 127 (8~15)行	
//[2]0 1 2 3 ... 127 (16~23)行	
//[3]0 1 2 3 ... 127 (24~31)行	
//[4]0 1 2 3 ... 127 (32~39)行	
//[5]0 1 2 3 ... 127 (40~47)行	
//[6]0 1 2 3 ... 127 (48~55)行	
//[7]0 1 2 3 ... 127 (56~63)行			   


static unsigned char SSD1306_buffer[SSD1306_BUFFER_SIZE] = {0x00};


/* IIC方式控制SSD1306 */
#if SSD1306_USE_IIC
/*******************************************************************
 * @name       :void SSD1306InitGPIO(void)
 * @date       :2018-08-27
 * @function   :初始化SSD1306的GPIO
 * @parameters :None
 * @retvalue   :None
********************************************************************/ 
void SSD1306InitGPIO(void)
{
    return;
}

/*******************************************************************
 * @name       :void SSD1306IOInit(u8 i2cId)
 * @date       :2023-02-24
 * @function   :初始化SSD1306的IIC，在此之前必须先初始化SSD1306的GPIO为IIC功能
                即在调用本函数之前先调用iot_gpio_iic.h中的初始化接口函数
 * @parameters :None
 * @retvalue   :None
********************************************************************/ 
void SSD1306InitIO(u8 i2cId)
{
    u32 ret = KP_ERR_SUCCESS;

    /* IIC初始化 */
    ret = IoTI2cInit(i2cId, OLED_IIC_BAUDRATE);
    if (ret != KP_ERR_SUCCESS) {
        IOT_PRINTF_FUNC("IoTI2cInit failed :%#x \r\n", ret); 
        return;
    }

    /* IIC设置波特率 */
    ret = IoTI2cSetBaudrate(i2cId, OLED_IIC_BAUDRATE);
    if (ret != KP_ERR_SUCCESS) {
        IOT_PRINTF_FUNC("IoTI2cSetBaudrate failed :%#x \r\n", ret); 
        return;
    }

    printf("SSD1306InitIO success \r\n");
}

/*******************************************************************
 * @name       :void OLED_Reset(void) 
 * @date       :2018-08-27
 * @function   :Reset OLED screen
 * @parameters :dat:0-Display full black
                    1-Display full white
 * @retvalue   :None
********************************************************************/ 
void SSD1306Reset(void)
{
    /* for I2C - do nothing */
}

u32 SSD1306WriteBuf(u32 iicId, u8 *data, u32 byteLen)
{
    u32 ret = IoTI2cWrite(iicId, SSD1306_I2C_ADDR, data, byteLen);
    if(ret != KP_ERR_SUCCESS){ 
        IOT_PRINTF_FUNC("IoTI2cWrite failed :%#x \r\n", ret);
        return ret;
    }

    return KP_ERR_SUCCESS;
}

u32 SSD1306WriteCmd(u32 iicId, u8 cmd)
{
    u32 ret = KP_ERR_SUCCESS;
    
    u8 buffer[] = {SSD1306_CTRL_CMD, cmd};
    ret = SSD1306WriteBuf(iicId, SSD1306_I2C_ADDR, buffer, sizeof(buffer));
    if(ret != KP_ERR_SUCCESS){ 
        IOT_PRINTF_FUNC("SSD1306WriteBuf failed :%#x \r\n", ret);
        return ret;
    }

    return KP_ERR_SUCCESS;
}

/*******************************************************************
 * @name       :u32 SSD1306WriteData(u32 iicId, u8 *data, u32 byteLen)
 * @date       :2018-08-27
 * @function   :Write a byte of content to the OLED screen
 * @parameters :dat:Content to be written
                dataType:0-write command
                         1-write data
 * @retvalue   :None
********************************************************************/
u32 SSD1306WriteData(u32 iicId, u8 *data, u32 byteLen)
{
    u32 ret = KP_ERR_SUCCESS;
    u8 buff[byteLen * 2] = {0};
    
    for (u32 i = 0; i < byteLen; i++) {
        buff[i*2] = SSD1306_CTRL_DATA | SSD1306_MASK_CONT;
        buff[i*2+1] = data[i];
    }
    buff[(byteLen - 1) * 2] = SSD1306_CTRL_DATA;
    ret = SSD1306WriteBuf(iicId, SSD1306_I2C_ADDR, buff, sizeof(buff));
    if(ret != KP_ERR_SUCCESS){ 
        IOT_PRINTF_FUNC("SSD1306WriteBuf failed :%#x \r\n", ret);
        return ret;
    }

    return KP_ERR_SUCCESS;
}

#endif


/* SPI方式控制SSD1306 */
#if SSD1306_USE_SPI
/*******************************************************************
 * @name       :void SSD1306InitGPIO(void)
 * @date       :2018-08-27
 * @function   :初始化SSD1306的GPIO
 * @parameters :None
 * @retvalue   :None
********************************************************************/ 
void SSD1306InitGPIO(void)
{
    return;
}

/*******************************************************************
 * @name       :void SSD1306IOInit(u8 spiId)
 * @date       :2023-02-24
 * @function   :初始化SSD1306的SPI，在此之前必须先初始化SSD1306的GPIO为SPI功能
                即在调用本函数之前先调用iot_gpio_spi.h中的初始化接口函数
 * @parameters :None
 * @retvalue   :None
********************************************************************/ 
void SSD1306InitIO(u8 spiId)
{
    u32 ret = KP_ERR_SUCCESS;

    /* 去初始化SPI */
    ret = IoTSpiDeinit(spiId);
    if (ret != KP_ERR_SUCCESS) {
        printf("IoTSpiDeinit failed :%#x \r\n", ret); 
        return;
    }

    IotSpiCfgInitParam spiInitParam = {0};
    spiInitParam.isSlave = FALSE;
    IotSpiCfgBasicInfo spiBasicInfo = {0};
    spiBasicInfo.cpol = HI_SPI_CFG_CLOCK_CPOL_0;
    spiBasicInfo.cpha = HI_SPI_CFG_CLOCK_CPHA_0;
    spiBasicInfo.framMode = HI_SPI_CFG_FRAM_MODE_MOTOROLA;
    spiBasicInfo.dataWidth = HI_SPI_CFG_DATA_WIDTH_E_8BIT;
    spiBasicInfo.endian = HI_SPI_CFG_ENDIAN_LITTLE;
    spiBasicInfo.freq = 2000000; /* defaul freq 2000000 Hz */
    
    //test_spi_printf("app_demo_spi_test_cmd_mw_sr Start");
    
    ret = IoTSpiInit(spiId, spiInitParam, &spiBasicInfo);
    if (ret != KP_ERR_SUCCESS) {
        printf("IoTSpiInit failed :%#x \r\n", ret); 
        return;
    }
    
    IoTSpiSetLoopBackMode(spiId, FALSE);
    
    osDelay(100);
    
    IoTSpiSetIrqMode(spiId, FALSE);
    IoTSpiSetDmaMode(spiId, FALSE);
    
    osDelay(100);

    printf("SSD1306InitIO success \r\n");
}

/*******************************************************************
 * @name       :void OLED_Reset(void) 
 * @date       :2018-08-27
 * @function   :Reset OLED screen
 * @parameters :dat:0-Display full black
                    1-Display full white
 * @retvalue   :None
********************************************************************/ 
void SSD1306Reset(void)
{
    SSD1306_RST_Set();
    delay_ms(100);
    SSD1306_RST_Clr();
    delay_ms(100);
    SSD1306_RST_Set();
    printf("SSD1306 reset \r\n");
}

u32 SSD1306WriteBuf(u32 spiId, u8 *data, u32 byteLen)
{
    u32 ret = IoTSpiHostWrite(spiId, data, byteLen);
    if(ret != KP_ERR_SUCCESS){ 
        IOT_PRINTF_FUNC("IoTSpiHostWrite failed :%#x \r\n", ret);
        return ret;
    }

    return KP_ERR_SUCCESS;
}

/*******************************************************************
 * @name       :u32 SSD1306WriteCmd(u32 spiId, u8 cmd)
 * @date       :2018-08-27
 * @function   :Write a byte of content to the OLED screen
 * @parameters :dat:Content to be written
                dataType:0-write command
                         1-write data
 * @retvalue   :None
********************************************************************/
u32 SSD1306WriteCmd(u32 spiId, u8 cmd)
{
    u32 ret = KP_ERR_SUCCESS;
    
    // 低电平写命令
    ret = SSD1306_DC_Clr();
    if(ret != KP_ERR_SUCCESS){ 
        IOT_PRINTF_FUNC("SSD1306_DC_Clr failed :%#x \r\n", ret);
        return ret;
    }

    // 低电平片选
    ret = SSD1306_CS_Clr();
    if(ret != KP_ERR_SUCCESS) { 
        IOT_PRINTF_FUNC("SSD1306_CS_Clr failed :%#x \r\n", ret);
        return ret;
    }

    // 写数据
    ret = SSD1306WriteBuf(spiId, cmd, U8_LEN);
    if(ret != KP_ERR_SUCCESS) { 
        IOT_PRINTF_FUNC("SSD1306WriteBuf failed :%#x \r\n", ret);
        return ret;
    }

    // 取消片选
    ret = SSD1306_CS_Set();
    if(ret != KP_ERR_SUCCESS) { 
        IOT_PRINTF_FUNC("SSD1306_CS_Set failed :%#x \r\n", ret);
        return ret;
    }

    return KP_ERR_SUCCESS;
}

/*******************************************************************
 * @name       :u32 SSD1306WriteByte(u32 spiId, u8 dat, u32 dataType)
 * @date       :2018-08-27
 * @function   :Write a byte of content to the OLED screen
 * @parameters :dat:Content to be written
                dataType:0-write command
                         1-write data
 * @retvalue   :None
********************************************************************/
u32 SSD1306WriteData(u32 spiId, u8 *data, u32 byteLen)
{
    u32 ret = KP_ERR_SUCCESS;
    
    // 高电平写数据
    ret = SSD1306_DC_Set();
    if(ret != KP_ERR_SUCCESS){ 
        IOT_PRINTF_FUNC("SSD1306_DC_Set failed :%#x \r\n", ret);
        return ret;
    }

    // 低电平片选
    ret = SSD1306_CS_Clr();
    if(ret != KP_ERR_SUCCESS) { 
        IOT_PRINTF_FUNC("SSD1306_CS_Clr failed :%#x \r\n", ret);
        return ret;
    }

    // 写数据
    ret = SSD1306WriteBuf(spiId, data, byteLen);
    if(ret != KP_ERR_SUCCESS) { 
        IOT_PRINTF_FUNC("Ssd1306WriteData failed :%#x \r\n", ret);
        return ret;
    }

    // 取消片选
    ret = SSD1306_CS_Set();
    if(ret != KP_ERR_SUCCESS) { 
        IOT_PRINTF_FUNC("SSD1306_CS_Set failed :%#x \r\n", ret);
        return ret;
    }

    return KP_ERR_SUCCESS;
}
#endif

/* Fills the Screenbuffer with values from a given buffer of a fixed length */
u32 SSD1306FillBuffer(u8* buf, u32 len) {
    u32 ret = KP_ERR_FAILURE;
    if (len <= SSD1306_BUFFER_SIZE) {
        memcpy(SSD1306_Buffer, buf, len);
        ret = KP_ERR_SUCCESS;
    }
    
    return ret;
}

/*******************************************************************
 * @name       :void OLED_Init(void)
 * @date       :2018-08-27
 * @function   :initialise OLED SH1106 control IC
 * @parameters :None
 * @retvalue   :None
********************************************************************/
void SSD1306Init(void)
{
    //SSD1306InitGPIO(); //初始化GPIO
    //delay_ms(200); 
    SSD1306Reset();     //复位OLED
    printf("%s,%s,%d \r\n", __FILE__, __func__, __LINE__);
	
	// Wait for the screen to boot
	osDelay(100);

/**************初始化SSD1306*****************/
    SSD1306WRCmd(0xAE); /*display off*/
    SSD1306WRCmd(0x00); /*set lower column address*/
    SSD1306WRCmd(0x10); /*set higher column address*/
    SSD1306WRCmd(0x40); /*set display start line*/ 
    SSD1306WRCmd(0xB0); /*set page address*/
    SSD1306WRCmd(0x81); /*contract control*/ 
    SSD1306WRCmd(0xFF); /*128*/
    SSD1306WRCmd(0xA1); /*set segment remap*/ 
    SSD1306WRCmd(0xA6); /*normal / reverse*/
    SSD1306WRCmd(0xA8); /*multiplex ratio*/ 
    SSD1306WRCmd(0x3F); /*duty = 1/64*/
    SSD1306WRCmd(0xC8); /*Com scan direction*/
    SSD1306WRCmd(0xD3); /*set display offset*/ 
    SSD1306WRCmd(0x00);
    SSD1306WRCmd(0xD5); /*set osc division*/ 
    SSD1306WRCmd(0x80);
    SSD1306WRCmd(0xD9); /*set pre-charge period*/ 
    SSD1306WRCmd(0XF1);
    SSD1306WRCmd(0xDA); /*set COM pins*/ 
    SSD1306WRCmd(0x12);
    SSD1306WRCmd(0xDB); /*set vcomh*/ 
    SSD1306WRCmd(0x30);
    SSD1306WRCmd(0x8D); /*set charge pump disable*/ 
    SSD1306WRCmd(0x14);
    SSD1306WRCmd(0xAF); /*display ON*/
    
    printf("%s,%s,%d \r\n", __FILE__, __func__, __LINE__);
}

/*******************************************************************
 * @name       :void OLED_Set_Pos(unsigned char x, unsigned char y) 
 * @date       :2018-08-27
 * @function   :Set coordinates in the OLED screen
 * @parameters :x:x coordinates
                y:y coordinates
 * @retvalue   :None
********************************************************************/
void SSD1306SetPos(unsigned char x, unsigned char y) 
{
    SSD1306WRCmd(YLevel+y/PAGE_SIZE);
    SSD1306WRCmd((((x+2)&0xf0)>>4)|0x10);
    SSD1306WRCmd(((x+2)&0x0f)); 
}  

/*******************************************************************
 * @name       :void OLED_Display_On(void) 
 * @date       :2018-08-27
 * @function   :Turn on OLED display
 * @parameters :None
 * @retvalue   :None
********************************************************************/ 	  
void SSD1306DisplayOn(void)
{
    SSD1306WRCmd(0X8D);  //SET DCDC命令
    SSD1306WRCmd(0X14);  //DCDC ON
    SSD1306WRCmd(0XAF);  //DISPLAY ON
}

/*******************************************************************
 * @name       :void OLED_Display_Off(void)
 * @date       :2018-08-27
 * @function   :Turn off OLED display
 * @parameters :None
 * @retvalue   :None
********************************************************************/    
void SSD1306DisplayOff(void)
{
    SSD1306WRCmd(0X8D);  //SET DCDC命令
    SSD1306WRCmd(0X10);  //DCDC OFF
    SSD1306WRCmd(0XAE);  //DISPLAY OFF
}

/*******************************************************************
 * @name       :void OLED_Set_Pixel(unsigned char x, unsigned char y,unsigned char color)
 * @date       :2018-08-27
 * @function   :set the value of pixel to RAM
 * @parameters :x:the x coordinates of pixel
                y:the y coordinates of pixel
                color:the color value of the point
                    1-white
                    0-black
 * @retvalue   :None
********************************************************************/ 
void SSD1306SetPixel(unsigned char x, unsigned char y,unsigned char color)
{
    if(x >= SSD1306_WIDTH || y >= SSD1306_HEIGHT) {
        // Don't write outside the buffer
        return;
    }
    
    if(color)
    {
        OLED_buffer[(y/PAGE_SIZE)*WIDTH+x] |= (1<<(y%PAGE_SIZE))&0xff;
    }
    else
    {
        OLED_buffer[(y/PAGE_SIZE)*WIDTH+x] &= ~((1<<(y%PAGE_SIZE))&0xff);
    }
}

/*******************************************************************
 * @name       :void OLED_Display(void)
 * @date       :2018-08-27
 * @function   :Display in OLED screen
 * @parameters :None
 * @retvalue   :None
********************************************************************/  
void SSD1306Display(void)
{
    u8 i,n;    
    for(i=0; i<PAGE_SIZE; i++)  
    {  
        SSD1306WRCmd(YLevel + i);     //设置页地址（0~7）
        SSD1306WRCmd(XLevelL);      //设置显示位置—列低地址
        SSD1306WRCmd(XLevelH);      //设置显示位置—列高地址
        SSD1306WRData(OLED_buffer[i * WIDTH + n], WIDTH);
        /*for(n=0; n<WIDTH; n++)
        {
            SSD1306WRByte(OLED_buffer[i*WIDTH+n], SSD1306_DATA);
        }*/
    }   //更新显示
}

/*******************************************************************
 * @name       :void OLED_Clear(unsigned dat)  
 * @date       :2018-08-27
 * @function   :clear OLED screen
 * @parameters :dat:0-Display full black
                    1-Display full white
 * @retvalue   :None
********************************************************************/ 
void SSD1306Clear(unsigned dat)
{  
    if(dat)
    {
        memset(OLED_buffer, 0xff, sizeof(OLED_buffer));
    }
    else
    {
        memset(OLED_buffer, 0, sizeof(OLED_buffer));
    }
    SSD1306Display();
}


